<?php
/**
 * @package base
 */

require_once '_dbobject.php';
require_once '_error.php';
require_once '_constants.php';

/**
 * Process data sent from forms.
 * Handle them.
 * Save them.
 */
class Redirector {
	private static $redirector;
	private static $requiredHistoryBack = null;
	
	private function __construct() {
	
	}
	
	/**
	 * Set up requirement to go back in a history.
	 * 
	 * @param $action - Action from Constants::REQ_ACTION_* to go back to
	 */
	private static function setRequiredHistoryBack (string $action) {
		self::$requiredHistoryBack = $action;
	}
	
	/**
	 * Get required page.
	 */
	public static function getRequiredHistoryBack () {
		return self::$requiredHistoryBack;
	}
	
	/**
	 * Init and process redirector class.
	 * string $type - type of sent data
	 */
	public static function init(string $type) {
		self::$redirector = new Redirector();
		self::$redirector->process($type);
	}
	
	/**
	 * Handle duplicate sending of the same form.
	 * It might occurs when refreshing page after submitting a form.
	 * Or when browsing in a history.
	 * Duplicate sent forms are dangerous, kick off them.
	 */
	private function handleDuplicateSentForms() {
		if (!self::verifyAndResetFormHash()) {
			Error::getError()->addErrorMessage(CodeManager::getCodeManager()->get("30068"));
			
			throw new ExceptionDuplicateData();
		}
	}
	
	/**
	 * Main method of this class
	 * Should be executed in case that data from a form are sent.
	 * 
	 * @see Constants::REQ_TYPE
	 * @param string $type - type of sent data
	 */
	private function process(string $type) {
		$error = Error::getError();
		$info = SiteContext::getSiteContext();
		$cm = CodeManager::getCodeManager();
		$logger = Logger::getLogger();
		try {
			switch($type) {
				case Constants::REQ_TYPE_OFFEREDIT:
					try {
						$this->handleDuplicateSentForms();
						
						/** we don't allow noone with lesser privileges to edit offers */
						Policy::verifyAndHandleUserMustBeAtLeastGroupRestricted();
						
						$t = DBConnectorMySQL::transactionBegin();
						$offer = new Offer();
						
						$offer->processValuesFromArrayWithCodes(null, Offer::$CODES_COLUMNS);
						
						if ($offer->seoUrl != null && $offer->seoUrl != "") {
							if (!SiteSeo::isSeoUrlValid($offer->seoUrl)) {
								throw new ExceptionDisplayable($cm->get("30218"));
							}
						}
						
						/** restricted can edit only offers assigned to them */
						if ($offer->agentId != $info->getCurrentUser()->id) {
							Policy::verifyAndHandleUserMustBeAtLeastGroupFull();
						}
						
						if (@is_string($_REQUEST["id"]) && $_REQUEST["id"] != "") {
							$offer->id = $_REQUEST["id"];
							$id = $offer->update();
						} else {
							$id = $offer->insert();
						}
						$offer->processImagesToDelete(null, $offer->imgDir());
						$applyWatermark = !isset($_POST["applyWatermark"]);
						$offer->processImagesFromArrayAndSaveThem(null, $offer->imgDir(), $applyWatermark);
						Logger::getLogger()->info("Added new offer with id = $id.");
						
						DBConnectorMySQL::transactionCommit($t);
						$error->addNoticeMessage($cm->get("30209"));
					} catch (Exception $e) {
						Logger::getLogger()->error($e->getMessage());
						Logger::getLogger()->error($e->getTraceAsString());
						
						if ($e instanceof ExceptionDBInsert || $e instanceof ExceptionQueryFailed) {
							/** If done any, roll back changes. */
							DBConnectorMySQL::transactionRollback();
							$error->addErrorMessage($cm->get("30212"));
							self::setRequiredHistoryBack(Constants::REQ_ACTION_OFFEREDIT);
						} elseif ($e instanceof ExceptionFileUploadFailed) {
							DBConnectorMySQL::transactionCommit($t);
							$error->addNoticeMessage($cm->get("30206"));
						} elseif ($e instanceof ExceptionDuplicateData) {
						} elseif ($e instanceof ExceptionDisplayable) {
							$error->addErrorMessage($e->getMessage());
							self::setRequiredHistoryBack(Constants::REQ_ACTION_OFFEREDIT);
						} else {
							/** If done any, roll back changes. */
							DBConnectorMySQL::transactionRollback();
							$error->addErrorMessage($cm->get("30212"));
							self::setRequiredHistoryBack(Constants::REQ_ACTION_OFFEREDIT);
						}
					}
				break;
				case Constants::REQ_TYPE_LOGIN:
					try {
						$this->handleDuplicateSentForms();
						
						/** everyone can log in */
						
						@$passwd = $_REQUEST['password'];
						@$loginSecure = mysql_real_escape_string($_REQUEST['login'], DBConnectorMySQL::getInitialResource());
						
						$connector = $info->getDBConnector();
						$res = $connector->query("select * from users where login = '$loginSecure' limit 0, 1");
						
						if ($user = User::mysqlFetch($res)) {
							$passwdHashSecure = User::createHash($passwd, substr($user->passwdHash, 0, strlen($user->passwdHash) / 2));
							if ($passwdHashSecure == $user->passwdHash) {
								$info->setCurrentUser($user);
								self::setRequiredHistoryBack(""); 
								return;
							}
						}
					} catch (Exception $e) {
						if ($e instanceof ExceptionDuplicateData) {
							break;
						} elseif ($e instanceof ExceptionQueryFailed) {
							$logger->error($e->getMessage());
							$logger->error($e->getTraceAsString());
							Error::getError()->addErrorMessage($cm->get("30069"));
						} else {
							$logger->error($e->getMessage());
							$logger->error($e->getTraceAsString());
							throw $e;
						}
					}
					Error::getError()->addErrorMessage("Access denied.");
					self::setRequiredHistoryBack(Constants::REQ_ACTION_LOGIN);
					$info->anonymousUserSet();
				break;
				case Constants::REQ_TYPE_LOGOUT:
					// TODO create Logout method for UserManager
					SiteContext::getSiteContext()->anonymousUserSet();
					session_unset();
					session_destroy();
					header( "HTTP/1.1 301 ".Constants::WEB_DEFAULTPAGE);
					header( "Status: 301 Bad Request" );
				break;
				case Constants::REQ_TYPE_SETTINGS:
					try {
						$this->handleDuplicateSentForms();
						
						/** we don't allow noone with lesser privileges to edit offers */
						Policy::verifyAndHandleUserMustBeAtLeastGroupRestricted();
						
						$connector = $info->getDBConnector();
						
						$settings = Settings::getSettings();
						$settings->processValuesFromArrayWithCodes(null, Settings::$CODES_COLUMNS);
						
						$settings->id = 0;
						
						$settings->update();
						
						$error->addNoticeMessage($cm->get("30217"));
					} catch (Exception $e) {
						if ($e instanceof ExceptionQueryFailed) {
							$logger->error($e->getMessage());
							$logger->error($e->getTraceAsString());
							$error->addErrorMessage($cm->get("30069"));
							self::setRequiredHistoryBack(Constants::REQ_ACTION_SETTINGS);
						} elseif ($e instanceof ExceptionDuplicateData) {
						} else {
							$logger->error($e->getMessage());
							$logger->error($e->getTraceAsString());
							throw $e;
						}
					}
				break;
				case Constants::REQ_TYPE_REGISTRATION:
					$isedit = false;
					try {
						$this->handleDuplicateSentForms();
						
						/** we don't want to let anonymous be registered */
						Policy::verifyAndHandleUserMustBeAtLeastGroupNone();
						
						$connector = $info->getDBConnector();
						
						if (@is_string($_REQUEST["id"]) && $_REQUEST["id"] != "") {
							$isedit = true;
						} else {
							$isedit = false;
						}
						
						$passwdSecure = mysql_real_escape_string($_REQUEST['password'], DBConnectorMySQL::getInitialResource());
						$passwdCopySecure = mysql_real_escape_string($_REQUEST['passwordc'], DBConnectorMySQL::getInitialResource());
						
						/** Pasword match. */
						if ($passwdCopySecure != $passwdSecure) {
							$error->addErrorMessage("Passwords doesn't match.");
							self::setRequiredHistoryBack(Constants::REQ_ACTION_REGISTATION);
							return;
						}
						
						/** Password strenght */
						if (!($isedit && $passwdCopySecure == "")) {
							if (is_array($arr = Policy::possiblePassword($passwdSecure, true))) {
								foreach($arr as $str) {
									$error->addErrorMessage($str);
								}
								self::setRequiredHistoryBack(Constants::REQ_ACTION_REGISTATION);
								return;
							}
						}
						
						$user = new User();
						$user->processValuesFromArrayWithCodes();
						if ($isedit) {
							if ($passwdSecure != "") {
								$user->passwdHash = User::createHash($passwdSecure);
							} else {
								$user->passwdHash = null;
							}
						} else {
							$user->passwdHash = User::createHash($passwdSecure);
						}
						
						if ($user->id != $info->getCurrentUser()->id) {
							/** only members of full group can modify others and create new accounts */
							Policy::verifyAndHandleUserMustBeAtLeastGroupFull();
						} else {
							/** users cannot change their group even when have full privileges - could lead to
								a state when everyone has low privileges */
							if ($user->group != $info->getCurrentUser()->group) {
								Policy::badPrivilegesError();
							}
						}
						
						$loginSecure = mysql_real_escape_string($user->login, DBConnectorMySQL::getInitialResource());
						
						//TODO escape all other things
						if ($loginSecure == "") {
							throw new ExceptionIllegalState("Empty login unacceptable.");
						}
						/** Is login ok? */
						if ($isedit) {
								$user->update();
						} else {
							if (!User::mysqlFetch($connector->query("select * from users where login = '$loginSecure'"))) {
								$user->insert();
								SiteContext::getSiteContext()->setCurrentUser($user);
							} else {
								$error->addErrorMessage($cm->get("30207", array($user->login)));
								self::setRequiredHistoryBack(Constants::REQ_ACTION_REGISTATION);
								break;
							}
						}
						$user->processImagesToDelete(null, $user->imgDir());
						$user->processImagesFromArrayAndSaveThem(null, $user->imgDir());
						
						foreach ($_POST as $member => $value) {
							if (preg_match("/offer_/", $member)) {
								$offer = new Offer;
								$offer->id = (int)preg_replace("/offer_/", "", $member);
								$offer->agentId = $info->getCurrentUser()->id;
								if ($offer->id > 0 && $offer->agentId > 0) {
									$offer->update();
								} else {
									self::setRequiredHistoryBack(Constants::REQ_ACTION_REGISTATION);
									$error->addErrorMessage("Cannot set current user id to the offer '$offer->id'");
								}
							}
						}
						$error->addNoticeMessage($cm->get("30210", array($loginSecure)));
					} catch (Exception $e) {
						if ($e instanceof ExceptionQueryFailed) {
							$logger->error($e->getMessage());
							$logger->error($e->getTraceAsString());
							$error->addErrorMessage($cm->get("30069"));
							self::setRequiredHistoryBack(Constants::REQ_ACTION_REGISTATION);
						} elseif ($e instanceof ExceptionFileUploadFailed) {
							$logger->error($e->getMessage());
							$logger->error($e->getTraceAsString());
							DBConnectorMySQL::transactionCommit($t);
							$error->addNoticeMessage($cm->get("30206"));
						} elseif ($e instanceof ExceptionDuplicateData) {
						} else {
							$logger->error($e->getMessage());
							$logger->error($e->getTraceAsString());
							throw $e;
						}
					}
				break;
			}
		} catch (ExceptionBadPrivileges $e) {
		}
	}
	/**
	 * Verify hash from the form and reset it.
	 * This was designed to prevent duplicate values inserted into the database.
	 * 
	 * @return bool
	 */
	private static function verifyAndResetFormHash() {
		$info = SiteContext::getSiteContext();
		$postedFormHash = @$_REQUEST['formHash'];
		$desiredFormHash = $info->getFormHash();
		if (is_string($desiredFormHash) && $desiredFormHash !== null 
				&& $postedFormHash == $desiredFormHash) {
			$info->setFormHash(null);
			return true;
		} else {
			return false;
		}
	}
}

?>